
/****************************************************************************
**
** Copyright (C) 2004-2008 VATATA.com . All rights reserved.
**
** This file is the part of the Miniutil Poject of VATATA.com.
** Vatata's miniutil library is lightweight and portable (for linux
** and Windows). The C++ Class Library included Threadpool, Dll danymic loading, 
** General socket application model and tcp server, httpserver/servlet, etc. 
** It is a pratical utility disigned for who don't want learn the Big C++ 
** Developing Platform, such as  Boost or ACE.
**
** This file may be used under the terms of the GNU General Public
** License versions 2.0 or 3.0 as published by the Free Software
** Foundation and appearing in the files LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file.  Alternatively you may (at
** your option) use any later version of the GNU General Public
** License if such license has been publicly approved by VATATA.com.
** (or its successors, if any) and the Miniutil Project Aministrators. 
**
** Please review the following information to ensure GNU General
** Public Licensing requirements will be met:
** http://www.gnu.org/licenses/lgpl.html. If you are unsure which 
** license is appropriate for your use, please review the following 
** information: http://code.google.com/p/miniutil/ or contact 
** http://www.vatata.com.
**
** In addition, as a special exception, VATATA.com, as the sole
** copyright holder for Miniutil Project, grants users of VATATA
** P2P Platform the right to release and package with the related 
** libraries without the source code.
**
** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE. vatata reserves all rights not expressly
** granted herein.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
****************************************************************************/



#include "miniutil/url.h"
#include "miniutil/thread.h"
#include "miniutil/buffer.h"

using namespace std;
using namespace miniutil;

#include <string.h>
#ifndef WIN32
#include <ctype.h>
#endif

void url::clear()
{
	this->queryParam.clear();
	this->host.clear();
    this->baseuri.clear();
	this->uri.clear();
	this->urlstr.clear();
	this->host.clear();
	this->proto.clear();
	this->queryString.clear();
	this->port = 0;
}



string url::encode(char * str, int len )
{
	size_t nlen = (len>0)? (size_t)len : strlen(str);
	miniutil::data::shared_buffer buf = miniutil::data::shared_buffer::safenew( nlen * 3 );
	char * dst = buf->data();
	for( size_t i=0; i<nlen; i++ )
	{
		if ( isalnum((unsigned char)str[i] ) )
			*dst++ = str[i];
		else if ( str[i] == ' ' )
			*dst++ = '+';
		else
		{
			*dst++ = '%';
			int hi = ((unsigned char)str[i]) >> 4;
			*dst++ = (char)( hi > 9 ? hi + 'A' - 10: hi + '0');
			int lo = str[i] & 0xf;
			*dst++ = (char)( lo > 9 ? lo + 'A' - 10: lo + '0');
		}
	}
	size_t elen = ( dst - buf->data() );
	string estr;
	estr.append( buf->data(), elen );
	return estr;
}

//
//string url::encode( char * str)
//{
//	char buf[2048];
//
//	char * dst = buf;
//	char * src = str;
//
//	while( *src )
//	{
//		if ( dst - buf > 2040 )
//		{
//			*dst = '\0';
//			return string(buf) + decode(src);
//		}
//
//		if ( isalnum( (unsigned char)*src ) )
//			*dst++ = *src;
//		else if ( isspace( (unsigned char)*src ) )
//			*dst++ = '+';
//		else
//		{
//			*dst++ = '%';
//			int hi = ((unsigned char)*src) >> 4;
//			*dst++ = (char)( hi > 9 ? hi + 'A' - 10: hi + '0');
//			int lo = *src & 0xf;
//			*dst++ = (char)( lo > 9 ? lo + 'A' - 10: lo + '0');
//		}
//		src ++;
//	}
//
//	*dst = '\0';
//
//	return string(buf);
//}

string url::decode(char * str , int len )
{
	size_t nlen = (len > 0 ) ? (size_t)len : strlen(str);
	miniutil::data::shared_buffer buf = miniutil::data::shared_buffer::safenew( nlen  );
	char * dst = buf->data();

	for( size_t i=0; i<nlen; i++ )
	{
		if ( str[i] == '+' )
			*dst++ = ' ';
		else if ( str[i] == '%' )
		{
			i++;
			int hi = toupper( str[i]) - '0';
			if ( hi < 0 ) break;
			else if ( hi > 9 ) hi = hi + '0' - 'A' + 10;
			if ( hi >= 16 ) break;
			i++;
			int lo = toupper( str[i] ) - '0';
			if ( lo < 0 ) break;
			else if ( lo > 9 ) lo = lo + '0' - 'A' + 10;
			if ( lo >= 16 ) break;

			*dst++ = ((unsigned char)hi<<4) + lo ;
		}
		else
			*dst++ = str[i];
	}
	size_t dlen = ( dst - buf->data() );
	string dstr;
	dstr.append( buf->data(), dlen );
	return dstr;
}

/*
string url::decode( char * str)
{
	char buf[2048];

	char * dst = buf;
	char * src = str;

	while( *src )
	{
		if ( dst - buf > 2040 )
		{
			*dst = '\0';
			return string(buf) + decode(src);
		}

		if( *src == '+' )
			*dst++ = ' ';
		else if ( *src == '%' )
		{
			int hi = toupper(*++src) - '0';
			if ( hi < 0 ) break;
			else if ( hi > 9 ) hi = hi + '0' - 'A' + 10;
			int lo = toupper(*++src) - '0';
			if ( lo < 0 ) break;
			else if ( lo > 9 ) lo = lo + '0' - 'A' + 10;

			*dst++ = ((unsigned char )hi << 4) + lo ;
		}
		else
			*dst++ = *src;
		src ++;
	}

	*dst = '\0';

	return string(buf);
}
*/
string url::decode( const string & str)
{
	return decode( (char *)str.data(), (int)str.length() );
}

string url::encode( const string & str)
{
	return encode( (char *)str.data(), (int)str.length() );
}


void url::parseURL(const string &urlStr )
{
	parseURL( urlStr.c_str() );
}

void url::parseURI(const string &uriStr )
{
	parseURI( uriStr.c_str() );
}

int url::defaultPort( string proto )
{
	if ( proto == "http" )
		return 80;
	else if ( proto == "rtsp" )
		return 554;

	return 0;
}

void url::parseURL( const char * urlStr)
{
	this->clear();
	if ( urlStr == NULL )
		return;

//	printf("\r\nparseURL() 1 : %s \r\n", urlStr);
//	thread::sleep(100);
//	string s(urlStr);
//	this->urlstr = s ;
//	printf("\r\nparseURL() 2 : \r\n");

	this->urlstr = urlStr ;

	string::size_type proto_end = this->urlstr.find("://");
	if ( proto_end == string::npos )
		return;
	this->proto = this->urlstr.substr(0, proto_end );
	for(int i=0; i< (int)this->proto.length(); i++ )
		this->proto[i] = tolower( this->proto[i] );

	string::size_type host_end = this->urlstr.find('/',proto_end + 3);
	if ( host_end == string::npos )
	{
		host_end = this->urlstr.find('?');
		if(host_end == string::npos)
			this->host = this->urlstr.substr(proto_end + 3 );
		else
			this->host = this->urlstr.substr(proto_end + 3 , host_end - proto_end -3 );
	}
	else
		this->host = this->urlstr.substr(proto_end + 3 , host_end - proto_end -3 );

	string::size_type port_pos = this->host.find(':');
	if ( port_pos != string::npos )
	{
		this->port = atoi( this->host.substr( port_pos + 1).c_str() );
		string nhost = this->host.substr(0, port_pos);
		this->host = nhost;
	}

	if ( this->port == 0 )
		this->port = (int) defaultPort( this->proto );

	if ( host_end == string::npos )
	{
		this->uri = "/";
		return;
	}

	string::size_type query_pos = this->urlstr.find('?', host_end);
	if ( query_pos == string::npos )
	{
		this->baseuri =  this->urlstr.substr( host_end ) ;
		this->uri = this->decode( baseuri );
		return;
	}
	else
	{
		this->baseuri = this->urlstr.substr( host_end , query_pos - host_end ) ;
		this->uri = this->decode( baseuri );
	}

	string::size_type last_pos = this->urlstr.find('#', host_end);
	if ( last_pos != string::npos && last_pos > query_pos )
		setQueryString ( this->urlstr.substr( query_pos + 1, last_pos - query_pos -1 ) );
	else
		setQueryString ( this->urlstr.substr( query_pos + 1 ) );
}

void url::parseURI( const char * uri_str)
{
	std::string uriStr(uri_str);
	string::size_type query_pos = uriStr.find('?');
	if ( query_pos == string::npos )
	{
		this->baseuri =  uriStr;
		this->uri = this->decode( baseuri );
		return;
	}
	else
	{
		this->baseuri = uriStr.substr( 0 , query_pos ) ;
		this->uri = this->decode( baseuri );
	}

	string::size_type last_pos = this->urlstr.find('#');
	if ( last_pos != string::npos && last_pos > query_pos )
		setQueryString ( uriStr.substr( query_pos + 1, last_pos - query_pos -1 ) );
	else
		setQueryString ( uriStr.substr( query_pos + 1 ) );
}

string & url::getQueryString()
{
	if ( this->queryString.empty() && this->queryParam.size() > 0 )
	{
		map<string,string>::iterator i;
		for( i = queryParam.begin(); i != queryParam.end(); i++ )
		{
			if ( i != queryParam.begin() )
				this->queryString += "&" ;
			this->queryString += (i->first) + "=" + (i->second);			
		}
	}

	return this->queryString;
}

void url::setParam(const string &key, const string & value)
{
	queryString.clear();
	queryParam[(key)] = (value);
}

void url::setParam(char *key, char *value)
{
	setParam(string(key),string(value) );
}


void url::setQueryString( const string & queryStr)
{
	this->queryString = queryStr;

	string::size_type sep = 0; 
	while(sep != string::npos && sep < queryString.length() )
	{
		string::size_type sep_end = queryString.find('&', sep );
		if ( sep_end == string::npos )
			sep_end = queryString.length();

		string::size_type eq_pos = queryString.find('=', sep );
		if ( eq_pos != string::npos && eq_pos < sep_end )
		{
			string key = (queryString.substr(sep,eq_pos-sep).c_str() ); 
			string value = ( queryString.substr( eq_pos + 1,  sep_end - eq_pos -1).c_str() );
			queryParam[(key)] = (value);
		}
		sep = sep_end + 1;
	}
}


string & url::getURL()
{
	this->urlstr = this->proto + "://" + this->host;
	if ( this->port != 0 )
	{
		char buf[20];
		sprintf(buf, ":%d", this->port);
		this->urlstr += buf;
	}

	this->urlstr += this->uri ;
	if ( getQueryString().length() > 0 )
		this->urlstr += '?' + queryString;

	return urlstr;
}

string url::getBaseURL()
{
	string str = this->proto + "://" + this->host;
	if ( this->port != 0 )
	{
		char buf[20];
		sprintf(buf, ":%d", this->port);
		str += buf;
	}

	str += (this->baseuri) ;
	return string(str);
}
